/*
 * TuplesProcessing.cpp
 *
 *  Created on: 31/01/2014
 *      Author: root
 */

#include "TuplesProcessing.h"

using namespace std;

//sacar a archivo de configuraciones
#define CHECK_IDLE_COND_WAIT_TIME_SECONDS 5 //???
#define MAX_TUPLE_LENGTH_TO_SHARE 0.5


extern int rank;
extern DppmParams * dppm;
extern Tuple * bestSolution;
extern bool end;
extern SortedBuffer * tuples;
extern pthread_mutex_t best_solution_mutex;
extern pthread_mutex_t tuples_mutex;
extern pthread_mutex_t end_flag_mutex;
extern pthread_cond_t idle_cond;

queue<Tuple*> AlmostFinished;

void EvalSolution(Tuple * newSol){

	pthread_mutex_lock(&best_solution_mutex);

	if (bestSolution->cost() <= newSol->cost()){
		delete newSol;
	}else{
		delete bestSolution;
		bestSolution = newSol;
	}

	pthread_mutex_unlock(&best_solution_mutex);
}

//esta funcion aplica la poda
bool canBeOpt(Tuple * cand){

	pthread_mutex_lock(&best_solution_mutex);
	Tuple * bestSolCopy = bestSolution->copy(); //copy reserva memoria
	pthread_mutex_unlock(&best_solution_mutex);

	if (bestSolCopy->empty()){
		return true;
	}

	int totMinTime = cand->time();
	int totMinCost = cand->cost();

	int numModes;
	int * times = NULL;
	int * costs = NULL;
	int minTime;
	int minCost;

	Tuple * candCopy = cand->copy();

	//obtengo el tiempo minimo posible para el resto de las activades sin asignar
	// y me fijo si se pasa del deadline
	for(int i = cand->lastAct() + 1; i < dppm->getNumActivities(); i++){

		//asigno la actividad
		candCopy->lastAct(i);

		numModes = dppm->getActivityNumModes(i);
		times = dppm->getActivityModesTimes(i);
		costs = dppm->getActivityModesCosts(i);

		//Todas las actividades tienen al menos un modo 0
		minTime = times[0];
		minCost = costs[0];
		//TODO ver orden de los modos segun costo o tiempo
		for(int j = 1; j < numModes; j++){
			if(times[j] < minTime){
				minTime = times[j]; //timpo del modo j en la act i
				//asigno el modo j para la act i
				candCopy->modes(i,j);
			}
			if(costs[j] < minCost){
				minCost = costs[j];
			}
		}

		//calculo el tiempo maximo que demoran los predecesores de la actividad i
		int * preds = dppm->getActivityPreds(i);
		int numPreds = dppm->getActivityNumPreds(i);
		int maxTimePreds = 0;
		for(int j = 0; j < numPreds; j++){
			maxTimePreds = maxTimePreds < candCopy->times(preds[j]) ? candCopy->times(preds[j]) : maxTimePreds;
		}

		candCopy->times( i , minTime + maxTimePreds );
		//Actualizo el costo

		totMinTime = totMinTime < candCopy->times(i) ? candCopy->times(i) : totMinTime;
		candCopy->time(totMinTime);
		totMinCost += minCost;

		if ( (totMinTime > dppm->getDeadline()) || (bestSolCopy->cost() <= totMinCost) ){
			delete candCopy;
			delete bestSolCopy;
			return false;
		}

	}

	delete candCopy;
	delete bestSolCopy; //faltaba
	return true;
}



void ProcessTupleStep(Tuple * T){

	Tuple * newTuple;
	int act = T->lastAct() + 1;

	//Predecesores la siguiente actividad
	int numPreds = dppm->getActivityNumPreds(act);
	int * preds = dppm->getActivityPreds(act);

	//Modos de la siguiente actividad
	int numModes = dppm->getActivityNumModes(act);
	int * modesTimes = dppm->getActivityModesTimes(act);
	int * modesCosts = dppm->getActivityModesCosts(act);

	for(int mode = 0 ; mode < numModes; mode++){

		//------- BRANCHING ------- //

		//Creo la nueva tupla a partir de una copia de la anterior
		newTuple = T->copy();

		//Actualizo la ultima actividad
		newTuple->lastAct(act);

		//Actualizo el modo de la nueva actividad
		newTuple->modes(act, mode);

		//Calculo el tiempo maximo que demoran sus predecesores
		int predsMaxTime = 0;
		for (int i = 0; i < numPreds; i++){
			if( newTuple->times(preds[i]) > predsMaxTime ){
				predsMaxTime = newTuple->times(preds[i]);
			}
		}

		//Actualizo el tiempo para la nueva actividad
		newTuple->times( act, predsMaxTime + modesTimes[mode] );

		//Actualiza el tiempo total de la nueva tupla
		if ( (predsMaxTime + modesTimes[mode]) > newTuple->time() ){
			newTuple->time( predsMaxTime + modesTimes[mode] );
		}

		//Actualizo el costo de la nueva tupla
		newTuple->cost( newTuple->cost() + modesCosts[mode] );


		//------- BOUNDING ------- //

		if( newTuple->isSolution() && (newTuple->time() <= dppm->getDeadline()) ){

			//Compara newTuple con bestSolution y en caso de que sea mejor actualiza bestSolution
			EvalSolution(newTuple);

		}else{

			if( (newTuple->times(act) <= dppm->getDeadline()) && canBeOpt(newTuple)){

				if ( (dppm->getNumActivities() - act - 1) > (MAX_TUPLE_LENGTH_TO_SHARE * dppm->getNumActivities()) ){

					tuples->push(newTuple);

				}else{

					AlmostFinished.push(newTuple);

				}

			}else{

				delete newTuple;

			}

		}
	}//for

	delete T;
}



int TuplesProcessing(){

	//debugPrinter(0,rank,"TuplesProcessing",137,"Comenzando processing");

	bool exitCondition = false;
	Tuple * tuple = NULL;

	struct timespec ts;
	ts.tv_sec = time(NULL) + CHECK_IDLE_COND_WAIT_TIME_SECONDS;

	do{
		//debugPrinter(0,rank,"TuplesProcessing",166,"Procesando");
		if( AlmostFinished.empty() ){
			////debugPrinter(0,rank,"TuplesProcessing",145,"AlmostFinished vacio");

			pthread_mutex_lock(&tuples_mutex);

			//Si tengo un prefijo para procesar
			if((tuple = tuples->pop()) != NULL){
				////debugPrinter(0,rank,"TuplesProcessing",151,"tuples NO vacio");
				ProcessTupleStep(tuple);

			}else{

				do{

					pthread_mutex_lock(&end_flag_mutex);
					exitCondition = end;
					pthread_mutex_unlock(&end_flag_mutex);
					pthread_cond_signal(&idle_cond);

				}while (!exitCondition &&
						(pthread_cond_timedwait(&idle_cond,&tuples_mutex,&ts) == ETIMEDOUT));

			}

			pthread_mutex_unlock(&tuples_mutex);

		}else{

			////debugPrinter(0,rank,"TuplesProcessing",177,"AlmostFinished NO vacio");

			tuple = AlmostFinished.front();
			AlmostFinished.pop();
			ProcessTupleStep(tuple);

		}
		//cout<<"TuplesProcessing: 251 , cantidad de tuplas: "<<AlmostFinished.size() + tuples->size()<<" , rank: "<<rank<<endl;
	}while(!exitCondition);

	//debugPrinter(0,rank,"TuplesProcessing",154,"Terminando rutina");

	return 0;

}



